package com.xucia.jsponic.datasource;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.Scriptable;

import com.xucia.jsponic.data.DataSourceHelper;
import com.xucia.jsponic.data.GlobalData;
import com.xucia.jsponic.data.ObjectId;
import com.xucia.jsponic.data.Persistable;
import com.xucia.jsponic.data.LazyPropertyId;
import com.xucia.jsponic.remote.StandardJsponFilter.LocalDataSource;

public class LocalJsponFileSource extends AbstractJsponSource implements LocalDataSource {
	public static final String LOCAL_JSPON_RELATIVE_PATH = "";
	private static String localJsponPath = "C:\\dev\\Authenteo";
	public static void setLocalJsponPath(String localJsponPath) {
		LocalJsponFileSource.localJsponPath = localJsponPath;
	}
	@Override
	protected String getJsponString(String objectId) throws Exception {
		Logger.getLogger(LocalJsponFileSource.class.toString()).info("Reading local jspon file " + localJsponPath + File.separatorChar + getId() + File.separatorChar + objectId);
		return getResourceAsString(new FileInputStream(localJsponPath + File.separatorChar + getId() + File.separatorChar + objectId));
	}
	@Override
	protected ObjectId idFromJSPONObject(JSONObject object) throws JSONException {
		// TODO Auto-generated method stub
		return null;
	}
	public Object getFieldValue(LazyPropertyId valueId) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}
	private Object intakeJspon(Object value) throws JSONException{
		if (value instanceof List)
			return value;
		if (value instanceof Persistable) {
			Persistable object = (Persistable)value;
			Object idObject = object.get("id");
			object.delete("id");
			if (idObject instanceof String) {
				PersistableInitializer initializer = DataSourceHelper.initializeObject(ObjectId.idForObject(this, (String) idObject));
				Object functionObject = object.get(GlobalData.FUNCTION_METHOD_FIELD);
				if (functionObject instanceof String) {
					initializer.initializeFunction((String) functionObject, !"client".equals(object.get("runAt"))); // we could just have this be true, it is safe to run local js files
				}
				object.delete(GlobalData.FUNCTION_METHOD_FIELD);
				Object prototypeObject = object.get(GlobalData.BASIS_FIELD);
				if (prototypeObject instanceof Persistable) {
					initializer.initializeByPrototype(((Persistable) intakeJspon(prototypeObject)).getId());
					object.delete(GlobalData.BASIS_FIELD);
				}
				final Object arrayObject = object.get("array",object);
				if (arrayObject instanceof JSONArray)				
					
					initializer.initializeList(new Iterator() {
						int i = 0;
						public boolean hasNext() {
							try {
								return ((JSONArray)arrayObject).get(i) != Scriptable.NOT_FOUND;
							} catch (JSONException e) {
								throw new RuntimeException(e);
							}
						}

						public Object next() {
							try {
								return intakeJspon(((JSONArray)arrayObject).get(i++));
							} catch (JSONException e) {
								throw new RuntimeException(e);
							}
						}

						public void remove() {
							throw new UnsupportedOperationException();
						}
						
					});
				else if (arrayObject instanceof JSONObject) { // not ready
					throw new UnsupportedOperationException();
				}
				
				object.delete("array");
				for (String key : object.keySet()){					
					initializer.setProperty(key, intakeJspon(object.get(key)));
				}
				return initializer.getInitializingObject().getId();
			}
		}
		return value;

	}
	public void mapObject(PersistableInitializer initializer, String objectId) throws Exception {
		Context.enter().putThreadLocal(ACCESS_LEVEL_MAP_KEY, new JSONObject()); // clear out the access level definitions
		Context.enter().putThreadLocal(SOURCE_URL_KEY,"");
		Object value ;
		try {
			value = Context.enter().evaluateString(GlobalData.getGlobalScope(), "temp=" + getJsponString(objectId), objectId, 1, null);
		}
		catch (EvaluatorException e) {
			e.printStackTrace();
			value = null;
		}
		if (value instanceof Persistable) 
			intakeJspon((Persistable)value);
		else
			throw new RuntimeException("Handling local values not implemented yet " + objectId);
		initializer.finished();
	}
	public void recordListAdd(String objectId, Object value) throws Exception {
		throw new UnsupportedOperationException("You can set a value on a local Jspon source");
	}
	public void recordListRemoval(String objectId, Object value) throws Exception {
		throw new UnsupportedOperationException("You can set a value on a local Jspon source");
	}
	public void recordPropertyAddition(String objectId, String name, Object value) throws Exception {
		throw new UnsupportedOperationException("You can set a value on a local Jspon source");
	}
	public void recordPropertyChange(String objectId, String name, Object value) throws Exception {
		throw new UnsupportedOperationException("You can set a value on a local Jspon source");
	}
	public void recordPropertyRemoval(String objectId, String name) throws Exception {
		throw new UnsupportedOperationException("You can set a value on a local Jspon source");
	}
	public LocalJsponFileSource() {
	}
	public void initParameters(JSONObject parameters) throws JSONException{

	}

	private static String getResourceAsString(InputStream is) throws IOException {
		BufferedReader in = new BufferedReader(new InputStreamReader(is));
		StringBuffer buffer = new StringBuffer();
		String line;
		while ((line = in.readLine()) != null) {
			buffer.append(line);
			buffer.append("\n");
		}
		return buffer.toString();
	}

}
